package com.tim.helper.sql;

import com.tim.ArgConstant;
import com.tim.util.CmdAndShUtil;
import com.tim.third.Dom4jXmlUtil;
import com.tim.util.StringAnalysisUtil;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class GenEntity {

    /**
     * 字符串替换修改配置文件
     * 运行plat-settle-impl插件mybatis-generator生成实体类、mapper、xml
     * 批量插入接口：
     * tk.mybatis.mapper.additional.insert.InsertListMapper 不支持主键策略
     * tk.mybatis.mapper.common.special.InsertListMapper 必须为自增主键
     */
    static int isTkMybatis = 2;// 0-baomidou，1-tk 2 自定义
    @Test
    public void genEntityAndMapper() throws Exception{
        // 0，本地h2；1，对账dev；
        useDb0();
//        useDb1();

        /* step1，参数配置*/
        String dbName="h2";
        String tableNames = "distribute_id_info";// 表名，逗号隔开
        int addDataAnno = 1;// 1添加@Data注解
        int toDisk = 1;// 输出到硬盘，以下项目信息无效
        String diskPath = "C:";


        // 输出路径
        String projectRootPath,project,srcPath,resourcePath,modelPkg,mapperPkg,xmlLoc;

        if(toDisk==1){
            project = "";
            srcPath = diskPath;
            resourcePath = diskPath;
            modelPkg = "aa";
            mapperPkg = "aa";
            xmlLoc = "aa";
            File srcPathDir = new File(srcPath);
            if(srcPathDir.exists()){
                // 清空指定文件夹；文件夹为空时才可删除
//                File delDir = new File(srcPath+"/aa");
//                Arrays.stream(delDir.listFiles())
//                        .forEach(File::delete);
            }else{
                srcPathDir.mkdirs();
            }
            System.out.println(srcPath+"/"+mapperPkg);
        }
        else{
            {
                // 秀商配置
                project = "biz-pig-impl";
                modelPkg = "com.xqxc.model.impl.pig.account.transport";// 实体包 biz
                xmlLoc = "biz.platform"// 自行从modelPkg中截取，从项目特性名开始；如mis、pub
                        .replaceAll("\\.", "/");// pub
                xmlLoc = "config/sqlMappers/"+xmlLoc;

                // 项目路径，切换项目时才修改
                if(project.contains("-pigear-")){
                    projectRootPath = "E:\\gitlab\\exodus_pigear\\biz\\"+project;// 输出路径
                    projectRootPath = projectRootPath.trim().concat("\\");
                    // 包名、类名后缀配置
                    mapperPkg = modelPkg.replace(".model.", ".dao.")
                            // 处理biz-pigear mapper包路劲
                            .replace("pigear.platform", "pigear.biz.platform");
                }else{
                    projectRootPath = "E:\\gitlab\\exodus\\biz\\biz-pig\\"+project;// 输出路径
                    projectRootPath = projectRootPath.trim().concat("\\");
                    // 包名、类名后缀配置
                    mapperPkg = modelPkg.replace(".model.", ".dao.")
                            // 处理biz-pigear mapper包路劲
                            .replace("pigear.platform", "pigear.biz.platform");
                }
                srcPath = projectRootPath+"src\\main\\java";
                resourcePath = projectRootPath+"src\\main\\resources";
            }
        }

        /* step2，修改插件配置文件；执行插件；修改生成文件*/
        String[] split = tableNames.trim()
                .replaceAll("\\s", "")
                .split(",");
        String dbUrl = String.format(DB_URL_TEMPLATE, dbHostPort, dbName);
        for(String tableName : split){
            if(tableName.isEmpty()){
                continue;
            }
            String modelName = StringAnalysisUtil.toPascal(tableName)
                    .concat(project.contains("-mis-")?"Mis":"").concat("DO");
            modifyPluginConfig(dbUrl, username, pwd, dbName, tableName, modelPkg, xmlLoc, mapperPkg, modelName, srcPath, resourcePath);

            runPlugin();
            /* 处理实体类，引入@Data*/
            modifyDOJavaFile(addDataAnno, getAttr(modelPathMap, dbName, 1, modelPkg), modelName, getAttr(modelPathMap, dbName, 0, srcPath));
        }

    }

    private void modifyPluginConfig(String dbUrl, String username, String pwd, String dbName, String tableName, String modelPkg, String xmlLoc, String mapperPkg, String modelName, String srcPath, String resourcePath) throws DocumentException, IOException, InterruptedException {
        /* step3，修改配置文件*/
        String configFilePath = ArgConstant.CLASSPATH_RESOURCE_PATH+ "\\maven\\mybatisGeneratorConfig.xml";
        modifyLine(configFilePath, 1);

        File configFile = new File(configFilePath);
        SAXReader reader = new SAXReader();
        Document document = reader.read(configFile);
        Element contextEle = document.getRootElement().element("context");
        Element plugin = contextEle.element("plugin");
        List<Element> props = plugin.elements("property");
        for(Element prop : props){
            if(prop.attributeValue("name").equals("mappers")){
                String mapper = isTkMybatis==0 ? "com.baomidou.mybatisplus.core.mapper.BaseMapper"
                        : isTkMybatis==1 ? "tk.mybatis.mapper.common.Mapper"
                        : "com.fbank.boot.component.base.mybatis.mapper.BaseMapper";
                prop.addAttribute("value", mapper);
                break;
            }
        }
        Element jdbcConnection = contextEle.element("jdbcConnection");
        jdbcConnection.addAttribute("driverClass", driverClass);
        jdbcConnection.addAttribute("connectionURL", dbUrl);
        jdbcConnection.addAttribute("userId", username);
        jdbcConnection.addAttribute("password", pwd);
        Element modelGenerator = contextEle.element("javaModelGenerator");
        modelGenerator.addAttribute("targetPackage", getAttr(modelPathMap, dbName, 1, modelPkg));
        modelGenerator.addAttribute("targetProject", getAttr(modelPathMap, dbName, 0, srcPath));
        Element xmlGenerator = contextEle.element("sqlMapGenerator");
        xmlGenerator.addAttribute("targetPackage", getAttr(xmlPathMap, dbName, 1, xmlLoc));
        xmlGenerator.addAttribute("targetProject", getAttr(xmlPathMap, dbName, 0, resourcePath));
        Element mapperGenerator = contextEle.element("javaClientGenerator");
        mapperGenerator.addAttribute("targetPackage", getAttr(mapperPathMap, dbName, 1, mapperPkg));
        mapperGenerator.addAttribute("targetProject", getAttr(mapperPathMap, dbName, 0, srcPath));
        Element table = contextEle.element("table");
        table.addAttribute("catalog", dbName);
        table.addAttribute("schema", dbName);
        table.addAttribute("tableName", tableName);
        table.addAttribute("domainObjectName", modelName);

        /* step4，输出xml文档*/
        String content = Dom4jXmlUtil.formatDocument(document);
        System.out.println(content);
        Files.write(Paths.get(configFilePath), content.getBytes());
        modifyLine(configFilePath, 0);
        Thread.sleep(1000);
    }

    private String getAttr(Map<String, String> map, String dbName, int isPkg, String defaultVar){
        String str = map.get(dbName);
        if(StringUtils.isBlank(str)){
            return defaultVar;
        }
        str = str.replaceAll("\\\\+", "/");
        String splitor = "";
        if(str.contains(SPLITOR_JAVA)){
            splitor = SPLITOR_JAVA;
        }
        else if(str.contains(SPLITOR_RESOURCE)){
            splitor = SPLITOR_RESOURCE;
        }
        else{
            return defaultVar;
        }
        int index = str.indexOf(splitor)+splitor.length();
        return isPkg==1 ? str.substring(index).replaceAll("/", ".")
                : str.substring(0, index);
    }

    private void modifyLine(String configFilePath, int type) throws IOException {
        Path configPath = Paths.get(configFilePath);
        List<String> list = Files.readAllLines(configPath);
        for (int i = 1; i < list.size(); i++) {
            String line = list.get(i).trim();
            if(type==1){
                if(line.startsWith("<!DOCTYPE")){
                    String replace = line.replaceAll("^<!DOCTYPE", "<!--<!DOCTYPE")
                            .replaceAll("dtd\">$", "dtd\">-->");
                    list.set(i, replace);
                    break;
                }
            }else{
                if(line.startsWith("<!--<!DOCTYPE")){
                    String replace = line.replaceAll("<!--", "")
                            .replaceAll("-->", "");
                    list.set(i, replace);
                    break;
                }
            }
        }
        Files.write(configPath, list, StandardOpenOption.SYNC);
    }

    private void runPlugin() throws DocumentException, IOException, InterruptedException {

        /* step5，运行插件。命令不对时，通过idea重新运行插件，复制*/
        // 考虑改为 mvn mybatis-generator:generate
//        String cmd = "L: && cd L:\\github\\Autil && E:\\jdk1.8\\bin\\java.exe -Dmaven.multiModuleProjectDirectory=L:\\github\\Autil -Dmaven.home=E:\\apache-maven-3.6.1 -Dclassworlds.conf=E:\\apache-maven-3.6.1\\bin\\m2.conf -Dmaven.ext.class.path=D:\\workSoftware\\ideaIU-2021.1.1.win\\plugins\\maven\\lib\\maven-event-listener.jar -javaagent:D:\\workSoftware\\ideaIU-2021.1.1.win\\lib\\idea_rt.jar=61272:D:\\workSoftware\\ideaIU-2021.1.1.win\\bin -Dfile.encoding=UTF-8 -classpath E:\\apache-maven-3.6.1\\boot\\plexus-classworlds-2.6.0.jar org.codehaus.classworlds.Launcher -Didea.version=2021.1.1 org.mybatis.generator:mybatis-generator-maven-plugin:1.3.2:generate";
        String cmd = System.getProperty("user.dir").replace(":", ": && cd ")+" && mvn mybatis-generator:generate";
        CmdAndShUtil.runBat(cmd);

    }

    interface DOFileTransferService {
        static List<String> transfer(List<String> modelContent) {
            int lastPrivateLine = 0;
            DOFileTransferService impl = isTkMybatis == 0 ? new BaomidouMybatisImpl()
                    : isTkMybatis == 1 ? new TkMybatisImpl()
                    : new CustomizeImpl();
            for (int i = 0; i < modelContent.size(); i++) {
                String line = modelContent.get(i);
                String trimedStr = line.trim();
                if (trimedStr.startsWith("import ")) {
                    line = impl.handleImportLine(line);
                } else if (trimedStr.startsWith("public") && !trimedStr.contains(" class ")) {
                    // public开头，非类声明时退出
                    break;
                } else if (trimedStr.startsWith("@Table")) {
                    line = impl.handleTableAnnoLine(line);
                } else if (trimedStr.startsWith("@Id")) {
                    line = impl.handleIdAnnoLine(line);
                } else if (trimedStr.startsWith("@Column")) {
                    line = impl.handleColumnAnnoLine(line);
                } else if (trimedStr.startsWith("private")) {
                    // 记录private行，无@Column时添加
                    lastPrivateLine = i;
                    String pre = modelContent.get(i - 1).trim();
                    line = impl.handleFieldLine(line, pre);
                }
                modelContent.set(i, line);
            }
            // 添加结尾反括号
            modelContent.set(lastPrivateLine, modelContent.get(lastPrivateLine) + "\n\n}");
            modelContent = modelContent.subList(0, lastPrivateLine + 1);
            return modelContent;
        }

        default String handleImportLine(String line){
            return line;
        }

        default String handleTableAnnoLine(String line){
            return line;
        }

        default String handleIdAnnoLine(String line){
            return line;
        }

        default String handleColumnAnnoLine(String line){
            return line;
        }

        default String handleFieldLine(String line, String pre){
            return line;
        }

        class TkMybatisImpl implements DOFileTransferService {

            @Override
            public String handleTableAnnoLine(String line) {
                // @Table注解前添加@Data
                line = "import lombok.Data;\n\n" +
                        "@Data\n" + line;
                return line;
            }
        }

        class BaomidouMybatisImpl implements DOFileTransferService {
            @Override
            public String handleImportLine(String line){
                return line.contains("javax.persistence.") ? "" : line;
            }

            @Override
            public String handleTableAnnoLine(String line) {

                // @Table注解前添加@Data
                String replace = line
                        .replace("@Table", "@TableName")
                        .replaceAll("name\\s*=\\s*", "");
                line = "import com.baomidou.mybatisplus.annotation.TableField;\n" +
                        "import com.baomidou.mybatisplus.annotation.TableName;\n" +
                        "import lombok.Data;\n\n" +
                        "@Data\n" + replace;
                return line;
            }

            @Override
            public String handleIdAnnoLine(String line) {
                return line.replace("@Id", "@TableField(\"id\")");
            }

            @Override
            public String handleColumnAnnoLine(String line) {
                return line.replace("@Column", "@TableField")
                        .replaceAll("name\\s*=\\s*", "");
            }

            @Override
            public String handleFieldLine(String line, String pre) {
                // 记录private行，无@TableField时添加
                if (!(pre.startsWith("@TableField"))) {
                    String varName = StringAnalysisUtil.getFieldName(line);
                    line = "\t@TableField(\"`" + StringAnalysisUtil.back2Subline(varName) + "`\")\n" + line;
                }
                return line;
            }
        }

        class CustomizeImpl extends TkMybatisImpl {
            @Override
            public String handleTableAnnoLine(String line) {
                if(line.contains(".")){
                    line = line.substring(0, line.indexOf('\"')+1)
                            + line.substring(line.lastIndexOf('.')+1);
                }
                return super.handleTableAnnoLine(line);
            }
        }
    }
    private void modifyDOJavaFile(int addDataAnno, String modelPkg, String modelName, String srcPath) throws IOException {
        if(addDataAnno ==1){
        }
        /* step1，读取文件*/
        String modelDir = String.format("%s/%s/", srcPath, modelPkg);
        modelDir = modelDir.replaceAll("\\.", "/");
        Path modelPath = Paths.get(modelDir, modelName + ".java");
        List<String> modelContent = null;
        try {
            modelContent = Files.readAllLines(modelPath);
        } catch (IOException e) {
            System.out.println(modelPath);
//                e.printStackTrace();
            modelContent = Files.readAllLines(modelPath, Charset.forName("GBK"));
        }
        modelContent = DOFileTransferService.transfer(modelContent);

        Files.write(modelPath, modelContent);// 写回
    }
    String DB_URL_TEMPLATE = "jdbc:mysql://%1$s/%2$s?useSSL=false";
    String driverClass="com.mysql.jdbc.Driver";
    String dbHostPort;
    String username;
    String pwd;
    Map<String, String> modelPathMap = new HashMap<>();
    Map<String, String> mapperPathMap = new HashMap<>();
    Map<String, String> xmlPathMap = new HashMap<>();
    String SPLITOR_JAVA = "/src/main/java/";
    String SPLITOR_RESOURCE = "/src/main/resources/";

    // 0，本地h2；jdbc:h2:tcp://localhost:9092/./h2
    private void useDb0(){
        dbHostPort = "localhost:3306";
        username = "root";
        pwd = "root";
        modelPathMap.put("", "");
        mapperPathMap.put("", "");
        xmlPathMap.put("", "");
    }

    // 1，对账dev；
    private void useDb1(){
        dbHostPort = "10.1.56.201:3306";
        username = "root";
        pwd = "AtaV60sc";
        modelPathMap.put("", "");
        mapperPathMap.put("", "");
        xmlPathMap.put("", "");
    }
}
